﻿using System.IO.Ports;
using System.Text;
using Devonline.Communication.Abstractions;
using Devonline.Communication.Modbus;
using Devonline.Communication.SerialPorts;
using Devonline.Core;
using Microsoft.Extensions.Logging;
using NModbus;
using NModbus.Serial;

namespace Devonline.Communication.ModbusRTU;

/// <summary>
/// Modbus RTU 协议基类实现
/// </summary>
public class ModbusRTUCommunicator : ModbusCommunicator, IModbusCommunicator, IStreamCommunicator
{
    protected readonly SerialPortOptions _serialPortOptions;
    protected SerialPort? _serialPort;

    public ModbusRTUCommunicator(ILogger<Communicator<byte[]>> logger, IModbusOptions options, SerialPortOptions serialPortOptions) : base(logger, options)
    {
        _serialPortOptions = serialPortOptions;
    }

    /// <summary>
    /// 当前连接状态
    /// </summary>
    public override bool IsConnected => _serialPort?.IsOpen ?? false;

    /// <summary>
    /// 重载的 stop 方法以在退出时自动关闭串口的连接
    /// </summary>
    /// <returns></returns>
    public override async Task StopAsync()
    {
        if (_serialPort == null)
        {
            return;
        }

        try
        {
            if (_serialPort.IsOpen)
            {
                _serialPort.Close();
            }

            _serialPort.Dispose();
            await base.StopAsync();
        }
        catch (Exception ex)
        {
            OnError(ex);
        }
    }

    /// <summary>
    /// 直接向串口发送 Modbus 协议报文的字节流
    /// </summary>
    /// <param name="data"></param>
    public override Task SendAsync(byte[] data) => Task.Run(() =>
    {
        ArgumentNullException.ThrowIfNull(_serialPort);
        if (_serialPort.IsOpen && data != null && data.Length > 0)
        {
            try
            {
                var value = data.ToHexString();
                _logger.LogDebug(LOG_COMMUNICATOR + $"数据 {value}" + " 将写入到串口 {com} 中", _options.Communicator, _serialPortOptions.PortName);
                _serialPort.Write(data, 0, data.Length);
                _logger.LogInformation(LOG_COMMUNICATOR + $"数据 {value}" + " 已写入到串口 {com} 中", _options.Communicator, _serialPortOptions.PortName);
                OnSend(data);
            }
            catch (Exception ex)
            {
                OnError(ex);
            }
        }
    });

    /// <summary>
    /// 打开串口, 并建立 Modbus 协议连接的过程
    /// </summary>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    protected override Task OpenAsync()
    {
        var portNames = SerialPort.GetPortNames();
        if (string.IsNullOrWhiteSpace(_serialPortOptions.PortName) || (!portNames?.Any(x => x == _serialPortOptions.PortName) ?? false))
        {
            _serialPortOptions.PortName = portNames?.FirstOrDefault() ?? string.Empty;
        }

        if (string.IsNullOrWhiteSpace(_serialPortOptions.PortName))
        {
            throw new Exception("缺少必须的串口名设置, 也没有读取到使用中的串口!");
        }

        _serialPort = new SerialPort(_serialPortOptions.PortName, _serialPortOptions.BaudRate, _serialPortOptions.Parity, _serialPortOptions.DataBits, _serialPortOptions.StopBits);
        if (!string.IsNullOrWhiteSpace(_serialPortOptions.Encoding))
        {
            _serialPort.Encoding = Encoding.GetEncoding(_serialPortOptions.Encoding);
        }

        ArgumentNullException.ThrowIfNull(_serialPort);
        if (!_serialPort.IsOpen)
        {
            _logger.LogWarning(LOG_COMMUNICATOR + " 正在打开串口 {com}", _options, _serialPortOptions.PortName);
            _serialPort.Open();
        }

        if (IsRunning && IsConnected)
        {
            _logger.LogDebug(LOG_COMMUNICATOR + " will create the Modbus master", _options);
            _modbusMaster = new ModbusFactory().CreateRtuMaster(_serialPort);
        }

        return Task.CompletedTask;
    }
}